/*file chiron src/browser/event.js */
/*preamble

    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    The license terms are stated in full in <license.rst> and at the end
    of all source files.

*/


include('base.js');
include('event.js');
include('environment.js');

/*** observe
    registers an event observer on a DOM element.
    defers to element.observe if that property exists.
    attempts to smoothe over cross-browser inconsistencies
    among event APIs.

    Returns an object analogous to that returned by
    the observe method on observables in that you
    can call its dismiss method to stop observing
    the named signal with the given observer.
*/
exports.observe = function (element, name, observer) {
    if (element.observe)
        return element.observe(name, observer);

    name = name.toLowerCase();

    /* from firebug */
    /* todo: review the reasoning and applicability of this
     * patch.  http://www.quirksmode.org/dom/events/index.html */
    /*
    if (isIE || isSafari) {
        if (name == 'keypress') {
            name = 'keydown';
        }
    }
    */

    var actualFunctor;

    if (isIE) {
        if (name != 'beforeunload') {
            name = "on" + name;
        }
        actualFunctor = function () {
            var event = window.event;
            event.stopPropagation = function () {
                event.cancelBubble = true;
            };
            event.cancelDefault = function () {
                event.returnValue = false;
            };
            event.stop = function () {
                event.cancelBubble = true;
                event.returnValue = false;
            };
            try {
                observer.call(event);
            } catch (exception) {
                error(exception);
            }
        };
        element.attachEvent(name, actualFunctor);
    } else {
        actualFunctor = function (event) {
            event.stop = function () {
                event.stopPropagation();
                event.preventDefault();
            };
            try {
                observer.call(event);
            } catch (exception) {
                error(exception);
            }
        };
        element.addEventListener(name, actualFunctor, false);
    }

    return {
        'dismiss': function () {
            if (isIE)
                element.detachEvent("on" + name, actualFunctor);
            else
                element.removeEventListener(name, actualFunctor, false);
        }
    };

};

/*** dismiss
*/
exports.dismiss = function (element, name, observer) {
    error('use the  object returned by observe to dismiss() an observer');
};

/*** observes
*/
exports.observes = function (element, observers) {
    itemsIter(observers).forEachArgs(function (name, observer) {
        observe(element, name, observer);
    });
};

/*** Widget
    A type that provides a leak-tight interface for observing
    DOM element events with JavaScript objects.  It also provides
    a narrower and more picky interface about observable
    events in an attempt to limit interactions with the DOM
    to those that can be safely captured across all supported
    browsers.
*/
exports.Widget = type([Observable], function (self, supr) {

    var element;

    /**** attach
    */
    self.attach = function (_element) {
        element = _element;
        element[widgetNs] = self;
    };

    /**** detach
    */
    self.detach = function () {
        element[widgetNs] = undefined;
        element = undefined;
    };

    /* a common action for event sinks.  */
    var action = function (event) {
        if (!self.getPropagation())
            event.stopPropagation();
        if (!self.getDefaulting())
            event.cancelDefault();
    };

    /**** observe
    */
    self.observe('observe', function (name, observer) {
        self.stop();
        if (sinks[name])
            sink(element, self, sinks[name], name);
        return supr.observe(name, observer);
    });

    /**** click */
    self.setSignal('click');
    /**** doubleClick */
    self.setSignal('doubleClick');
    /**** mouseMove */
    self.setSignal('mouseMove');
    /**** mouseOver */
    self.setSignal('mouseOver');
    /**** mouseOut */
    self.setSignal('mouseOut');
    /**** mouseUp */
    self.setSignal('mouseUp');
    /**** mouseDown */
    self.setSignal('mouseDown');
    /**** mouseEnter */
    self.setSignal('mouseEnter');
    /**** mouseLeave */
    self.setSignal('mouseLeave');
    /**** mouseScroll */
    self.setSignal('mouseScroll');
    /**** mouseScrollClick */
    self.setSignal('mouseScrollClick');

});

var sinks = {
    'click': 'click',
    'doubleClick': 'dblclick',
    'mouseMove': 'mousemove',
    'mouseOver': 'mouseover',
    'mouseOut': 'mouseout',
    'mouseUp': 'mouseup',
    'mouseDown': 'mousedown'
};

var widgetNs = 'chironjs.org/widget';
var sinkNs = 'chironjs.org/sink';

/*
     - element - a DOM element to forward events from
     - widget - a JavaScript observable object to forward events to
     - browserName - the name of the event in the browser
     - chironName - the normalized name of the event
*/
var sink = function (element, widget, browserName, chironName) {
    try {

        if (element[widgetNs] && element[widgetNs] != widget)
            element[widgetNs].detach();
        element[widgetNs] = widget;

        var sink = element[sinkNs] || Set();
        element[sinkNs] = sink;

        if (!sink.has(browserName)) {
            observe(element, browserName, function () {
                var node = this.target;
                while (!node[widgetNs])
                    node = node.parentNode;
                node[widgetNs][chironName](this);
            });
            sink.insert(browserName);
        }

    } finally {
        /* destroy reference cycles */
        widget = undefined;
        element = undefined;
    }
};

/*todo wheel events */

/*

http://adomas.org/javascript-mouse-wheel/

function wheel(event){
        var delta = 0;
        if (!event) // IE
                event = window.event;
        if (event.wheelDelta) { // IE Opera
                delta = event.wheelDelta/120;
                if (window.opera)
                        delta = -delta;
        } else if (event.detail) { // MOZ
                delta = -event.detail/3;
        }
        if (delta)
                handle(delta);
        if (event.preventDefault)
                event.preventDefault();
    event.returnValue = false;
}

if (window.addEventListener)
// Moz
        window.addEventListener('DOMMouseScroll', wheel, false);
// IE Opera
window.onmousewheel = document.onmousewheel = wheel;

*/


/*license

    Legal
    =======
    
    Chiron is a component of the Tale web-game project.
    
    See <credit.txt> for a complete list of
    contributions and their licenses.  All contributions are provided
    under permissive, non-viral licenses including MIT, BSD, Creative Commons
    Attribution 2.5, Public Domain, or Unrestricted.
    
    
    License
    =======
    
    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    
    MIT License
    -----------
    
    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

*/

